<?php

namespace Wgx\PdoEx;

/**
 * 这个类是 pdoex 整个库的入口类
 * 主要用于 初始化 和提供 后续用于操作数据库的 pdo 实例
 */
final class PdoManager
{
    /** @var null|PdoManager */
    private static $singleObj = null;

    /** @var array */
    private $config;
    /** @var RwSplitConnNameGetterInterface */
    private $rwSplitConnNameGetter;
    /** @var null|string */
    private $defaultName;
    /** @var array */
    private $name2PdoArr;

    /**
     * 初始化 PdoManager 同时也是初始化 pdoex 整个库
     * 初始化过程中会创建一个 PdoManager 的实例
     * @param array $config - 数据库连接配置
     * @param RwSplitConnNameGetterInterface $rwSplitConnNameGetter
     */
    public static function init($config, RwSplitConnNameGetterInterface $rwSplitConnNameGetter)
    {
        self::$singleObj = new PdoManager($config, $rwSplitConnNameGetter);
    }

    /**
     * 返回初始化过程中创建的实例
     * @return null|PdoManager
     */
    public static function getSingleObj()
    {
        return self::$singleObj;
    }

    /**
     * PdoManager constructor.
     * @param array $config
     */
    private function __construct($config, RwSplitConnNameGetterInterface $rwSplitConnNameGetter)
    {
        $this->config = $config;
        $this->rwSplitConnNameGetter = $rwSplitConnNameGetter;
        $this->name2PdoArr = array();
    }

    /**
     * @return array
     */
    public function getConfig()
    {
        return $this->config;
    }

    /**
     * @param null|string $name - 配置文件中数据库连接名, 不传使用默认连接名
     * @return null|\PDO
     */
    private function getPdoByName($name = null)
    {
        if (empty($name)) {
            $name = $this->getDefaultName();
            if (empty($name)) {
                return null;
            }
        }

        if (empty($this->name2PdoArr[$name])) {
            if (empty($this->config[$name])) {
                return null;
            }
            $config = $this->config[$name];
            $dsn = empty($config['dsn']) ? null : trim($config['dsn']);
            if (empty($dsn)) {
                return null;
            }

            if (empty($config['options'])) {
                $pdo = new \PDO($dsn,
                    empty($config['user']) ? '' : trim($config['user']),
                    empty($config['password']) ? '' : trim($config['password'])
                );
            } else {
                $pdo = new \PDO($dsn,
                    empty($config['user']) ? '' : trim($config['user']),
                    empty($config['password']) ? '' : trim($config['password']),
                    $config['options']
                );
            }

            $this->name2PdoArr[$name] = $pdo;
        }

        return $this->name2PdoArr[$name];
    }

    /**
     * 本函数用 主库连接名 获取 特定用于读 或 特定用于读写 的 pdo 实例
     * 逻辑实现过程中需要先用 主库连接名 获取 特定用于读 或 特定用于读写 的连接名 , 这个会交由 PdoManager::init 中传入的 RwSplitConnNameGetterInterface 接口实例来完成
     * @param null|string $rwConnName - 主库连接名, 不传使用默认连接名
     * @param string $sqlMode - SqlModeEnum
     * @return null|\PDO
     */
    public function getPdo($rwConnName = null, $sqlMode = SqlModeEnum::READ_WRITE)
    {
        if (empty($rwConnName)) {
            $rwConnName = $this->getDefaultName();
            if (empty($rwConnName)) {
                return null;
            }
        }

        $name = $this->rwSplitConnNameGetter->getConnName($rwConnName, $sqlMode);
        if (empty($name)) {
            return null;
        }

        return $this->getPdoByName($name);
    }

    /**
     * @return null|string
     */
    public function getDefaultName()
    {
        if ($this->defaultName) {
            return $this->defaultName;
        }

        $config = $this->config;
        if (empty($config)) {
            return null;
        }

        foreach ($config as $n => $c) {
            if (is_array($c) && !empty($c['default'])) {
                $this->defaultName = $n;

                return $this->defaultName;
            }
        }

        if (!empty($config['default'])) {
            if (is_string($config['default'])) {
                $this->defaultName = $config['default'];

                return $this->defaultName;
            } else {
                $this->defaultName = 'default';

                return $this->defaultName;
            }
        }

        $this->defaultName = array_keys($config)[0];

        return $this->defaultName;
    }


}